﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class Difference : BaseImageFilter, ITwoSourceFilter
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Difference"/> class.
        /// </summary>
        public Difference()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Difference"/> class.
        /// </summary>
        /// <param name="_difference">The _difference.</param>
        internal Difference(Difference _difference)
        {
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            Process(_bitmap, _bitmap);
            return _bitmap;
        }

        /// <summary>
        /// Shows only the differences left. Regions that are equal
        /// in both bitmaps will be blacked out
        /// </summary>
        /// <param name="bmpimg"></param>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public UnsafeBitmap Process(UnsafeBitmap bmpimg, UnsafeBitmap bmp)
        {
            BitmapData bmpData = bmpimg.BitmapData; //bmpimg.LockBits(new Rectangle(0, 0, bmpimg.Width, bmpimg.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData bmpData2 = bmpimg.BitmapData; //bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int width = bmpData.Width;
            int height = bmpData.Height;

            if (bmpData2.Width > width)
            {
                width = bmpData2.Width;
            }

            if (bmpData2.Height > height)
            {
                height = bmpData2.Height;
            }

            bmpimg.Dispose();
            bmp.Dispose();

            Bitmap bit1 = new Bitmap(bmpimg.InternalBitmap, width, height);
            Bitmap bit2 = new Bitmap(bmp.InternalBitmap, width, height);

            Bitmap bmpresult = new Bitmap(width, height);

            BitmapData data1 = bit1.LockBits(new Rectangle(0, 0, bit1.Width, bit1.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData data2 = bit2.LockBits(new Rectangle(0, 0, bit2.Width, bit2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData data3 = bmpresult.LockBits(new Rectangle(0, 0, bmpresult.Width, bmpresult.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            unsafe
            {
                int nByteCount = bmpimg.BytesPerPixelCount;
                int remain1 = data1.Stride - data1.Width * nByteCount; //data1.Stride - data1.Width * 3;
                int remain2 = data2.Stride - data2.Width * nByteCount; //data2.Stride - data2.Width * 3;
                int remain3 = data3.Stride - data3.Width * nByteCount; //data3.Stride - data3.Width * 3;

                byte* ptr1 = (byte*)data1.Scan0;
                byte* ptr2 = (byte*)data2.Scan0;
                byte* ptr3 = (byte*)data3.Scan0;

                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width * nByteCount; j++) //for (int j = 0; j < width * 3; j++)
                    {
                        if (ColorFunctions.IsInColorTolerance(Color.FromArgb(ptr1[0], ptr1[1], ptr2[2]), Color.FromArgb(ptr2[0], ptr2[1], ptr2[2]), 10, 10, 10))
                        {
                            ptr3[0] = (byte)0xFF;
                            ptr3[1] = (byte)0xFF;
                            ptr3[2] = (byte)0xFF;
                        }
                        else
                        {
                            ptr3[0] = 0xFF;
                            ptr3[1] = 127;
                            ptr3[2] = 0;
                        }
                        ptr1++;
                        ptr2++;
                        ptr3++;
                    }
                    ptr1 += remain1;
                    ptr2 += remain2;
                    ptr3 += remain3;
                }
            }

            bit1.UnlockBits(data1);
            bit2.UnlockBits(data2);
            bmpresult.UnlockBits(data3);

            return bmpresult;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Difference(this);
        }
    }
}
